package main

import (
	"fmt"
	"io"
	"net"
	"os"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"
)

/**
本程序为用户可以直接访问的中介服务器上运行的穿透服务端
工作流程：
	1启动 ControlPort 的监听，等待在内网环境运行的受控端连接
		（内网主机要连接到这个端口等待控制指令和心跳）
	2启动 ListenPort 的监听，等待用户的web请求
	3启动 TunnelPort 的监听，等待内网受控端链接隧道
	4启动定时释放任务，清楚过期链接和无效链接
	5创建一个阻塞等待配对器组装
用户访问时：
	1. ListenPort 接受到用户的请求，
		通过addConnMatchAccept新建（未完全配置，tunnel为空）一个配对器ConnMatch到全局map connListMap中，
	2.通过ControlPort向内网被控主机发送一个链接通知“new\n”
	3.内网主机在接收到消息后，打通真实web服务器和TunnelPort的隧道
	4.监听在TunnelPort的程序得到一个新的请求链接
	5.在makeForward方法中通过configConnListTunnel方法将1步骤创建的不完整的配对器补充完整
	6.通过向通道connListMapUpdate中传值通信，运行tcpForward中joinConn方法，将来用户的链接和来自内网客户端的链接绑定
从而实现内网穿透
*/

var (
	//与安装在内网服务器的client通信长链接接口
	ControlPort = "0.0.0.0:8009"
	//链路实际通信端口
	TunnelPort = "0.0.0.0:8008"
	//用户通信监听端口,即用户眼中的web服务器地址
	ListenPort = "0.0.0.0:888"
)

var controllerConnection *net.TCPConn = nil

func main() {
	loadConfig()
	println("ControlPort", ControlPort)
	println("TunnelPort", TunnelPort)
	println("ListenPort", ListenPort)
	//监听控制端口8009
	go makeControl()
	go keepAlive()
	go controllerKeepLive()
	//监听转发端口8008
	go makeForward()
	//监听Web服务端口888
	go makeAccept()
	//定时释放连接
	go releaseConnMatch()
	//内存检测
	go checkMem()
	//执行tcp转发
	tcpForward()
}

func loadConfig() {
	content, err := os.ReadFile("./conf.conf")
	if err != nil {
		return
	}
	mp := regexp.MustCompile("(.+)?=[0-9:\\.]+")
	matches := mp.FindAll(content, -1)
	for _, match := range matches {
		if strings.HasPrefix(string(match), "ControlPort=") {
			ControlPort = string(match)[len("ControlPort="):]
		}
		if strings.HasPrefix(string(match), "TunnelPort=") {
			TunnelPort = string(match)[len("TunnelPort="):]
		}
		if strings.HasPrefix(string(match), "ListenPort=") {
			ListenPort = string(match)[len("ListenPort="):]
		}
	}
}

func makeControl() {
	var tcpAddr *net.TCPAddr
	tcpAddr, _ = net.ResolveTCPAddr("tcp", ControlPort)
	//打开一个tcp断点监听
	tcpControllerListener, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		panic(err)
	}
	fmt.Println("控制端口已经监听")
	for {
		tcpConn, err := tcpControllerListener.AcceptTCP()
		if err != nil {
			panic(err)
		}
		fmt.Println("新的客户端连接到控制端服务进程:" + tcpConn.RemoteAddr().String())
		if controllerConnection != nil {
			fmt.Println("已经存在一个客户端连接!")
			//直接关闭掉多余的客户端请求
			controllerConnection.Close()
		}
		controllerConnection = tcpConn
	}
}
func controllerKeepLive() {
	for {
		if controllerConnection != nil {
			//一旦有客户端连接到服务端的话，服务端每隔2秒发送hi消息给到客户端
			//如果发送不出去，则认为链路断了，清除cache连接
			_, e := controllerConnection.Write(([]byte)("hi\n"))
			if e != nil {
				fmt.Println("客户端连接断开")
				controllerConnection.Close()
				return
				//cache = nil
			}
		}
		time.Sleep(time.Second * 2)
	}
}

func makeAccept() {
	var tcpAddr *net.TCPAddr
	tcpAddr, _ = net.ResolveTCPAddr("tcp", ListenPort)
	tcpWebListener, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		panic(err)
	}
	defer tcpWebListener.Close()
	for {
		tcpConn, err := tcpWebListener.AcceptTCP()
		tcpConn.SetKeepAlive(false)
		tcpConn.SetDeadline(time.Now().Add(time.Minute * 10))
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Println("A client connected 8007:" + tcpConn.RemoteAddr().String())
		addConnMatchAccept(tcpConn)
		sendMessage("new\n")
	}
}

// 配对器
type ConnMatch struct {
	accept        *net.TCPConn //8007 tcp链路 accept
	acceptAddTime int64        //接受请求的时间
	tunnel        *net.TCPConn //8008 tcp链路 tunnel
}

var connListMap = make(map[string]*ConnMatch)
var lock = sync.Mutex{}

func addConnMatchAccept(accept *net.TCPConn) {
	//加锁防止竞争读写map
	lock.Lock()
	defer lock.Unlock()
	now := time.Now().UnixNano()
	connListMap[strconv.FormatInt(now, 10)] = &ConnMatch{accept, time.Now().Unix(), nil}
}

func sendMessage(message string) {
	fmt.Println("send Message " + message)
	if controllerConnection != nil {
		_, e := controllerConnection.Write([]byte(message))
		if e != nil {
			fmt.Println("消息发送异常")
			fmt.Println(e.Error())
		}
	} else {
		fmt.Println("没有客户端连接，无法发送消息")
	}
}

func makeForward() {
	var tcpAddr *net.TCPAddr
	tcpAddr, _ = net.ResolveTCPAddr("tcp", TunnelPort)
	tcpListener, err := net.ListenTCP("tcp", tcpAddr)
	if err != nil {
		panic(err)
	}
	defer tcpListener.Close()
	fmt.Println("隧道服务开始监听，等待隧道连接 ...")
	for {
		tcpConn, err := tcpListener.AcceptTCP()
		tcpConn.SetKeepAlive(false)
		tcpConn.SetDeadline(time.Now().Add(time.Minute * 10))
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Println("隧道端口接收到来自内网的连接 :" + tcpConn.RemoteAddr().String())
		configConnListTunnel(tcpConn)
	}
}

var connListMapUpdate = make(chan int)

func configConnListTunnel(tunnel *net.TCPConn) {
	//加锁解决竞争问题//todo
	lock.Lock()
	used := false
	for _, connMatch := range connListMap {
		//找到tunnel为nil的而且accept不为nil的connMatch
		if connMatch.tunnel == nil && connMatch.accept != nil {
			//填充tunnel链路
			connMatch.tunnel = tunnel
			used = true
			//这里要break，是防止这条链路被赋值到多个connMatch！
			break
		}
	}
	if !used {
		//如果没有被使用的话，则说明所有的connMatch都已经配对好了，直接关闭多余的8008链路
		fmt.Println(len(connListMap))
		_ = tunnel.Close()
		fmt.Println("关闭多余的tunnel")
	}
	lock.Unlock()
	//使用channel机制来告诉另一个方法已经就绪
	connListMapUpdate <- 0
}
func tcpForward() {
	for {
		select {
		case <-connListMapUpdate:
			lock.Lock()
			for key, connMatch := range connListMap {
				//如果两个都不为空的话，建立隧道连接
				if connMatch.tunnel != nil && connMatch.accept != nil {
					fmt.Println("建立tcpForward隧道连接")
					joinConn(connMatch.accept, connMatch.tunnel)
					//从map中删除
					delete(connListMap, key)
				}
			}
			lock.Unlock()
		}
	}
}
func joinConn(conn1 *net.TCPConn, conn2 *net.TCPConn) {
	f := func(local *net.TCPConn, remote *net.TCPConn) {
		//使用io.Copy传输两个tcp连接，
		_, err := io.Copy(local, remote)
		if err != nil {
			fmt.Println(err.Error())
			return
		}
		fmt.Println("通信结束，隧道解耦")
	}
	go f(conn2, conn1)
	go f(conn1, conn2)
}

func releaseConnMatch() {
	for {
		lock.Lock()
		for key, connMatch := range connListMap {
			//如果在指定时间内没有tunnel的话，则释放该连接
			if connMatch.tunnel == nil && connMatch.accept != nil {
				if time.Now().Unix()-connMatch.acceptAddTime > 5 {
					fmt.Println("释放超时连接")
					err := connMatch.accept.Close()
					if err != nil {
						fmt.Println("释放连接的时候出错了:" + err.Error())
					}
					delete(connListMap, key)
				}
			}
		}
		lock.Unlock()
		time.Sleep(5 * time.Second)
	}
}

func keepAlive() {
	//for {
	//	time.Sleep(time.Minute)
	//	http.Get(fmt.Sprintf("http://127.0.0.1%s/", ListenPort))
	//}
}

func checkMem() {
	for {
		var m runtime.MemStats
		runtime.ReadMemStats(&m)
		fmt.Println("Memory Allocation:", m.Alloc, "\tTotal Allocation:", m.TotalAlloc, "\tSys:", m.Sys, "\tHeap Allocation:", m.HeapAlloc, "\tHeap System:", m.HeapSys)
		//手动清理缓存垃圾
		runtime.GC()
		time.Sleep(time.Second * 100)
	}
}
